package com.kyl.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.kyl.base.PageResponse;
import com.kyl.constant.SuperConstant;
import com.kyl.dto.DeptDto;
import com.kyl.dto.PostDto;
import com.kyl.entity.Dept;
import com.kyl.entity.Post;
import com.kyl.mapper.DeptMapper;
import com.kyl.mapper.PostMapper;
import com.kyl.service.DeptService;
import com.kyl.service.PostService;
import com.kyl.utils.BeanConv;
import com.kyl.utils.EmptyUtil;
import com.kyl.utils.NoProcessing;
import com.kyl.vo.DeptVo;
import com.kyl.vo.PostVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 职位表服务实现类
 */
@Service
public class PostServiceImpl implements PostService {
    @Resource
    private PostMapper postMapper;

    @Resource
    private DeptService deptService;

    @Autowired
    DeptMapper deptMapper;

    /**
     * 多条件查询职位分页列表
     *
     * @param postDto  查询条件
     * @param pageNum  当前页码值
     * @param pageSize 每页显示条数
     * @return
     */
    @Override
    public PageResponse<PostVo> findPostPage(PostDto postDto, int pageNum, int pageSize) {
        //1、启动分页助手，进行分页设置
        PageHelper.startPage(pageNum, pageSize,"sort_no asc, create_time desc");

        //2、检查部门编号是否为空
        if (EmptyUtil.isNullOrEmpty(postDto.getDeptNo())) throw new RuntimeException("部门不能为空");

        //3、执行查询
        Page<List<Post>> page= postMapper.selectPage(postDto);

        //把查询结果转换为分页对象
        PageResponse<PostVo> pageResponse = PageResponse.of(page, PostVo.class);

        //4、对查询结果进行判空,不为空就做进一步操作
        if (!EmptyUtil.isNullOrEmpty(pageResponse.getRecords())){
            //提取所有部门编号,通过lambda表达式，.map(PostVo::getDeptNo)提取部门编号，重新组合成一个列表
            List<String> deptNoList = pageResponse.getRecords().stream().map(PostVo::getDeptNo).collect(Collectors.toList());
            //根据部门编号查询部门信息
            List<DeptVo> deptVoList = deptService.findDeptInDeptNos(deptNoList);
            //遍历查询结果，设置创建日期和关联部门信息
            pageResponse.getRecords().forEach(postVo -> {
                //设置创建日期
                postVo.setCreateDay(LocalDateTimeUtil.format(postVo.getCreateTime(), "yyyy-MM-dd"));
                //设置关联部门信息
                deptVoList.forEach(d -> {
                    if (postVo.getDeptNo().equals(d.getDeptNo())) {
                        postVo.setDeptVo(d);
                    }
                });
            });
        }

        //返回结果
        return pageResponse;
    }

    /**
     * 创建职位信息
     *
     * 该方法首先将传入的PostDto对象转换为Post对象，然后生成职位编号并设置到Post对象中，
     * 接着调用postMapper的insert方法将Post对象插入数据库如果插入失败，则抛出运行时异常
     * 最后，将插入成功的Post对象转换为PostVo对象，并根据部门编号查询相关部门信息，如果存在，
     * 则将查询到的部门信息设置到PostVo对象中，然后返回PostVo对象
     *
     * @param postDto 职位信息的DTO对象，包含要创建的职位的相关信息
     * @return PostDto 返回创建成功的职位信息的VO对象
     * @throws RuntimeException 如果职位信息保存出错，则抛出此异常
     */
    @Override
    public PostVo createPost(PostDto postDto) {
        //转换PostVo为Post
        Post post = BeanUtil.toBean(postDto, Post.class);
        //生成职位编号
        String postNo = createPostNo(post.getDeptNo());
        post.setPostNo(postNo);
        //插入数据库
        int flag = postMapper.insert(post);
        //检查插入结果
        if (flag==0){
            throw new RuntimeException("保存职位信息出错");
        }
        //转换为PostVo对象
        PostVo postVoResult = BeanConv.toBean(post, PostVo.class);
        //装配部门
        DeptDto deptDto = DeptDto.builder()
                .dataState(SuperConstant.DATA_STATE_0)
                .parentDeptNo(postDto.getDeptNo()).build();
        List<Dept> deptList = deptMapper.selectList(deptDto);
        //检查部门列表是否为空
        if (!EmptyUtil.isNullOrEmpty(deptList)){
            postVoResult.setDeptVo(BeanConv.toBean(deptList.get(0),DeptVo.class));
        }
        //返回创建成功的职位信息
        return postVoResult;
    }

    /**
     * 更新岗位信息
     *
     * @param postDto 岗位数据传输对象，包含要更新的岗位信息
     * @return 如果更新成功，返回true；否则抛出异常
     *
     * 此方法首先将PostDto对象转换为Post对象，然后检查岗位的状态
     * 如果岗位状态为启用（数据状态为"1"），则检查该岗位是否已分配给用户
     * 如果岗位已分配，则抛出异常，表示不能禁用该岗位
     * 如果岗位未分配或状态为禁用，則更新数据库中的岗位信息
     * 如果更新操作失败，抛出异常表示更新出错
     */
    @Override
    public Boolean updatePost(PostDto postDto) {
        //转换PostVo为Post
        Post post = BeanUtil.toBean(postDto, Post.class);
        //检查岗位状态，如果为启用状态，则进一步检查是否已分配给用户
        if (post.getDataState().equals("1")) {
            //准备查询参数，用于检查岗位是否已分配
            String[] s = {""};
            s[0] = post.getPostNo();
            //查询使用该岗位的用户总数
            Integer total = checkPostHasUser(s);
            //如果岗位已分配给用户，抛出异常，表示不能禁用该岗位
            if(total>0){
                throw new RuntimeException("岗位已分配,不能禁用");
            }
        }
        //执行更新操作，如果更新失败，抛出异常
        int flag = postMapper.updateByPrimaryKey(post);
        if (flag==0){
            throw new RuntimeException("修改职位信息出错");
        }
        //更新成功，返回true
        return true;
    }

    /**
     * 根据PostDto对象查询岗位列表，并转换为PostVo对象列表
     * 此方法首先设置查询条件为数据状态正常，然后调用postMapper的selectList方法获取Post对象列表，
     * 最后将这些对象转换为PostVo对象列表返回
     *
     * @param postDto 查询条件对象，包含需要查询的岗位相关信息
     * @return PostVo 对象列表，包含查询到的岗位信息
     */
    @Override
    public List<PostVo> findPostList(PostDto postDto) {
        // 设置查询条件为数据状态正常
        postDto.setDataState("0");
        // 调用postMapper的selectList方法获取Post对象列表
        List<Post> postList = postMapper.selectList(postDto);
        // 将Post对象列表转换为PostVo对象列表
        List<PostVo> postVoList = BeanConv.toBeanList(postList, PostVo.class);

        // 检查PostVo对象列表是否不为空
        if (!EmptyUtil.isNullOrEmpty(postVoList)){
            // 提取所有岗位对应的部门编号
            List<String> deptNos = postVoList.stream().map(PostVo::getDeptNo).collect(Collectors.toList());
            // 根据部门编号列表查询相关部门信息
            List<DeptVo> deptVoList = deptService.findDeptInDeptNos(deptNos);
            // 为每个PostVo对象装配对应的部门信息
            postVoList.forEach(n->{
                deptVoList.forEach(d->{
                    if (n.getDeptNo().equals(d.getDeptNo())){
                        n.setDeptVo(BeanConv.toBean(d,DeptVo.class));
                    }
                });
            });
        }
        // 返回装配完成的PostVo对象列表
        return postVoList;
    }

    /**
     * 根据用户ID查找该用户相关的PostVo列表
     *
     * @param userId 用户ID，用于查询特定用户的相关帖子信息
     * @return List<PostVo> 返回一个PostVo对象的列表，这些对象包含与指定用户ID相关的帖子信息
     */
    @Override
    public List<PostVo> findPostVoListByUserId(Long userId) {

        return postMapper.findPostVoListByUserId(userId);
    }
    /**
     * 创建岗位编号
     * 该方法根据部门编号生成岗位编号，确保每个岗位拥有唯一的标识
     * @param deptNo 部门编号，用于生成岗位编号的基础
     * @return 返回生成的岗位编号字符串
     */
    private String createPostNo(String deptNo) {
        // 根据部门编号构建查询条件
        PostDto postDto = PostDto.builder().deptNo(deptNo).build();
        // 查询该部门下所有岗位列表
        List<Post> postList = postMapper.selectList(postDto);
        // 如果该部门下没有岗位，则创建并返回新的岗位编号
        if (EmptyUtil.isNullOrEmpty(postList)){
            return NoProcessing.createNo(deptNo,false);
            // 如果该部门下已有岗位，则找到最大岗位编号并累加，然后返回新的岗位编号
        }else {
            Long postNo = postList.stream()
                    .map(post -> { return Long.valueOf(post.getPostNo());})
                    .max(Comparator.comparing(i -> i)).get();
            return NoProcessing.createNo(String.valueOf(postNo),true);
        }
    }

    /**
     * 根据岗位ID数组删除岗位信息
     * 此方法首先检查每个岗位是否有关联的用户，如果没有关联用户，则进行批量删除
     *
     * @param postIds 岗位ID数组，用于指定需要删除的岗位
     * @return 删除操作影响的行数
     * @throws RuntimeException 如果岗位已分配给用户，则抛出运行时异常，防止删除关联的岗位
     */
    @Override
    public int deletePostByIds(String[] postIds) {
        //验证当前岗位是否被用户关联
        Integer total = checkPostHasUser(postIds);
        //如果岗位有关联的用户，则不允许删除，抛出异常
        if(total>0){
            throw new RuntimeException("岗位已分配,不能删除");
        }
        //调用Mapper方法，根据岗位ID数组删除岗位信息
        return postMapper.deletePostByIds(postIds);
    }

    /**
     * 校验岗位是否分配用户
     * 此方法用于检查给定的岗位ID数组中，每个岗位是否至少分配了一名用户
     * 主要用于在进行岗位相关操作前，确保这些岗位是有效的，即有用户被指派
     *
     * @param postIds 岗位ID数组，代表需要检查的多个岗位
     * @return 返回一个整数，表示已分配用户的岗位数量如果返回值大于0，说明所有岗位都已分配用户；如果为0，说明没有岗位分配用户；如果为null，可能表示输入的岗位ID数组为空
     */
    public Integer checkPostHasUser(String[] postIds) {
        return postMapper.checkPostHasUser(postIds);
    }

}
